/*
 * # 应用所有存储过程:
 *  Authority - 组织与用户
 * 
 * # 描述:
 *  为应用提供组织、工作岗位、用户的管理功能，以及针对组织、部门、岗位和用户的权限分派和验证功能，同时提供用户登录、注销、导航等基础功能。
 * 
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

 use "pittypat_app";
 go

/*
 * # 存储过程 - Sql Server:
 *   Login - 登录
 * 
 * # 描述:
 *   使用指定的用户名和密码登录到系统。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_authentication_login"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_authentication_login" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_authentication_login"
	@name nvarchar(50), -- 登录名称 
	@password nvarchar(30), -- 登录密码 
	@token binary(36) out, -- 用户令牌 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @pk bigint = null;
    declare @id binary(16) = null;
    declare @calcCount int = 0;
    declare @_token binary(36) = null;
    declare @cipher binary(36) = null;
	declare @now datetime2 = sysdatetime();

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 初始化 out 参数 @token (用户令牌)
	set @token = null;
	
	-- 验证登录名称和密码
    if @name is null or @name = '' or @password is null or len(@password) < 6
	begin
		-- 没有提供有效的参数
		set @_message = N'用户名和登录密码都不能是空的，并且密码长度不能小于 6。';
        return;
    end
    
    if "waln_authority"."is_mobile"(@name) = 1
		-- 手机号码
		select @pk = "PK", @id = "Id", @cipher = "Password"
		from "waln_authority"."users"
		where "Mobile" = @name and "LockTime" is null;
	else
		if "waln_authority"."is_email"(@name) = 1
			-- 电子邮件地址
			select @pk = "PK", @id = "Id", @cipher = "Password"
			from "waln_authority"."users"
			where "Email" = @name and "LockTime" is null;
		else
			-- 登录名称
			select @pk = "PK", @id = "Id", @cipher = "Password"
			from "waln_authority"."users"
			where "LoginName" = @name and "LockTime" is null;
    
    if @pk is null
	begin
		-- 没有找到目标用户，用户未注册或者已经被锁定
		set @_message = N'用户不存在，或者已经被锁定。';
        return;
    end
    
    -- 验证密码
    if "waln_authority"."check_password"(@password, @cipher) = 0
	begin
		-- 密码不正确
		set @_message = N'密码不正确。';
        return;
    end
    
    -- 计算用户令牌
    while @calcCount < 3
	begin
		set @_token = crypt_gen_random(36);
        set @calcCount = @calcCount + 1;
        if not exists (select * from "waln_authority"."users" where "Token" = @_token)
			set @calcCount = 3;
		else
			if @calcCount >= 3
			begin
				-- 已经计算三次 token，都出现重复
				set @_message = N'用户令牌重复。';
				return;
			end
    end
    
    -- 更新令牌
    update "waln_authority"."users"
    set 
		"Token" = @_token,
        "LoginTime" = @now,
		"TokenExpireTime" = dateadd("MINUTE", 30, @now) -- 有效期 30 分钟
	where
		"PK" = @pk;

	set @token = @_token;
    
    select * from "waln_authority"."v_authentication_user" where "PK" = @pk;	
    
    -- 指示操作成功
    set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Logout - 注销
 * 
 * # 描述:
 *   注销已经登录的用户。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_authentication_logout"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_authentication_logout" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_authentication_logout"
	@token binary(36), -- 用户令牌 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 验证参数 @token (用户令牌)
	if @token is not null and datalength(@token) > 0
	begin
		-- 将令牌和令牌有效期设置为 null
		update 
			"waln_authority"."users" 
		set 
			"Token" = null,
			"TokenExpireTime" = null 
		where 
			"Token" = @token;
	end

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Demand - 执行权限检查
 * 
 * # 描述:
 *   验证当前用户是否对目标功能拥有权限。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_authentication_demand"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_authentication_demand" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_authentication_demand"
	@token binary(36), -- 用户令牌 
	@path nvarchar(128), -- 功能路径 
	@authorized bit out, -- 是否已授权 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;
	
	-- 功能隶属的目标应用的 Id
	declare @appId binary(16) = null;
	-- 功能隶属的目标模块的 Id
	declare @modId binary(16) = null;
    -- 功能开发者代码
	declare @developerCode varchar(30) = null;
    -- 功能隶属应用的标识符
	declare @appIdentifier varchar(30) = null;
    -- 功能隶属模块的标识符
	declare @modIdentifier varchar(30) = null;
    -- 索引位置1
    declare @i1 int = -1;
    -- 索引位置2
    declare @i2 int = -1;
    -- 索引位置3
    declare @i3 int = -1;
    -- 用户主键
    declare @user_pk bigint = null;
    -- 用户的 Id
    declare @userId binary(16);
    -- 当前日期时间
    declare @now datetime2 = sysdatetime();
    -- 用户令牌有效期
    declare @expire datetime2 = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 默认返回用户未被授权的指示
    set @authorized = 0;
    
    -- 验证参数 @token (用户令牌)
    if @token is null or datalength(@token) = 0
	begin
		-- 在没有提供用户令牌时，认为用户是匿名用户，不返回任何用户信息，并且总是认为匿名用户未被授权
		set @_success = 1;
        return;
	end
    
    if @path is null or @path = ''
	begin
		-- 没有提供功能路径时，仅仅返回用户信息
        -- 根据用户令牌查询用户信息
		select @user_pk = "PK", @userId = "Id", @expire = "TokenExpireTime" 
        from "waln_authority"."users" 
        where "Token" = @token and 
			"LockTime" is null and -- 未被锁定
			"TokenExpireTime" >= @now; -- 令牌有效
		
        -- 如果没有查询到用户信息，则表示用户不存在或者尚未登录，此时直接成功返回
		if @user_pk is null
		begin
			set @_success = 1;
			return;
		end
	end
    else
	begin
		-- 提供了功能路径时，检查用户对目标功能是否拥有权限
		-- 功能路径中第一个 '.' 的位置
		set @i1 = charindex('.', @path);
		if @i1 is null or @i1 <= 1
		begin
			-- 没有提供有效的路径
			return;
		end
		
		-- 功能路径中第二个 '.' 的位置
		set @i2 = charindex('.', @path, @i1 + 1);
		if @i2 is null or @i2 <= @i1 + 1
		begin
			-- 没有提供有效的路径
			return;
		end
		
		-- 功能路径中第三个 '.' 的位置
		set @i3 = charindex('.', @path, @i2 + 1);
		if @i3 is null or @i3 <= @i2 + 1
		begin
			-- 没有提供有效的路径
			return;
		end
		
        -- 第一个 '.' 之前的部分为开发者代码
		set @developerCode = left(@path, @i1 - 1);
        -- 第一个 '.' 和第二个 '.' 之间为应用标识符
		set @appIdentifier = substring(@path, @i1 + 1, @i2 - @i1 - 1);
        -- 第二个 '.' 和第三个 '.' 之间为模块标识符
		set @modIdentifier = substring(@path, @i2 + 1, @i3 - @i2 - 1);
		
        -- 开发者代码、应用标识符和模块标识符必须是有效的标识符
		if "waln_authority"."is_identifier"(@developerCode, 30) = 0 or 
		   "waln_authority"."is_identifier"(@appIdentifier, 30) = 0 or
		   "waln_authority"."is_identifier"(@modIdentifier, 30) = 0
		begin
			-- 没有提供有效的路径
            set @_message = @path + N' 不是有效的功能路径。';
			return;
		end
		
        -- 根据用户令牌查询用户信息
		select @user_pk = "PK", @userId = "Id", @expire = "TokenExpireTime" 
        from "waln_authority"."users" 
        where "Token" = @token and 
			"LockTime" is null and -- 未被锁定
			"TokenExpireTime" >= @now; -- 令牌有效
        
        -- 如果没有查询到用户信息，则表示用户不存在或者尚未登录，此时直接成功返回
		if @user_pk is null
		begin
			set @_success = 1;
			return;
		end;
		
		if @developerCode = 'waln' and @appIdentifier = 'authority' and @modIdentifier in ('authentication', 'profile')
		begin
			-- 基础应用的权限验证和用户资料总是对所有用户开放权限
			set @authorized = 1;
		end
		else
		begin
			-- 查询应用的 Id
			select @appId = "Id" from "waln_authority"."apps" where "DeveloperCode" = @developerCode and "Identifier" = @appIdentifier;

			if @appId is not null
			begin
				-- 找到了 App
				select @modId = "Id" from "waln_authority"."modules" where "AppId" = @appId and "Identifier" = @modIdentifier;

				if @modId is not null
				begin
					-- 找到了模块
					if  exists (select "PK" from "waln_authority"."permissions" where "ModuleId" = @modId and "RoleType" = 4 and "RoleId" = @userId and ("ExpireTime" is null or @now <= "ExpireTime")) or -- 用户自身
						exists (select p."PK" from "waln_authority"."permissions" as p join "waln_authority"."userposts" as u on p."RoleId" = u."OrgId" and p."RoleType" = 1 where p."ModuleId" = @modId and u."UserId" = @userId and (p."ExpireTime" is null or @now <= p."ExpireTime")) or -- 组织
						exists (select p."PK" from "waln_authority"."permissions" as p join "waln_authority"."userposts" as u on p."RoleId" = u."DeptId" and p."RoleType" = 2 where p."ModuleId" = @modId and u."UserId" = @userId and (p."ExpireTime" is null or @now <= p."ExpireTime")) or -- 部门
						exists (select p."PK" from "waln_authority"."permissions" as p join "waln_authority"."userposts" as u on p."RoleId" = u."PostId" and p."RoleType" = 3 where p."ModuleId" = @modId and u."UserId" = @userId and (p."ExpireTime" is null or @now <= p."ExpireTime")) -- 岗位
					begin
						-- 拥有权限
						set @authorized = 1;
					end
					else
					begin
						-- 没有权限
						set @authorized = 0;
					end
				end
				else
				begin
					set @_message = N'没有找到目标模块。';
				end
			end
			else
			begin
				set @_message = N'没有找到目标应用。';
			end
		end
    end

	-- 如果用户令牌有效期不足 10 分钟，则延长 30 分钟
	if dateadd("MINUTE", 10, @now) > @expire
		update "waln_authority"."users"
		set "TokenExpireTime" = dateadd("MINUTE", 30, "TokenExpireTime")
		where "PK" = @user_pk;

	-- 返回 Demand.User
	select 
		v.* 
	from 
		"waln_authority"."v_authentication_user" as v 
	where 
		v."PK" = @user_pk; 

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   NavList - 用户导航列表
 * 
 * # 描述:
 *   加载指定用户拥有权限的 App 和模块的列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_authentication_navlist"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_authentication_navlist" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_authentication_navlist"
	@token binary(36), -- 用户令牌 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    declare @userId binary(16) = null;
    declare @now datetime2 = sysdatetime();

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 验证参数 @token (用户令牌)
	if @token is null or datalength(@token) = 0
	begin
		set @_message = N'没有提供 用户令牌。';
		return;
	end
    
    -- 根据用户令牌查询用户信息
	select @userId = "Id"
    from "waln_authority"."users" 
    where "Token" = @token and 
		"LockTime" is null and -- 未被锁定
		"TokenExpireTime" >= @now; -- 令牌有效

    if @userId is null
	begin
		-- 没有找到用户
		set @_message = N'没有提供找到用户。';
		return;
	end
	
    -- 返回用户有权限的模块隶属的应用的列表
	select distinct
        v.* 
    from 
        "waln_authority"."v_authentication_app" as v 
	join "waln_authority"."modules" as m on m."AppId" = v."Id" and m."Presentation" = 1
    join "waln_authority"."permissions" as p on p."ModuleId" = m."Id" and (p."ExpireTime" is null or p."ExpireTime" >= @now) and 
		((p."RoleType" = 4 and p."RoleId" = @userId) or 
		 (p."RoleType" = 3 and p."RoleId" in (select distinct "PostId" from "waln_authority"."userposts" where "UserId" = @userId and "PostId" is not null)) or 
		 (p."RoleType" = 2 and p."RoleId" in (select distinct "DeptId" from "waln_authority"."userposts" where "UserId" = @userId and "DeptId" is not null)) or 
         (p."RoleType" = 1 and p."RoleId" in (select distinct "OrgId" from "waln_authority"."userposts" where "UserId" = @userId and "OrgId" is not null)))
    order by 
        v."Position" asc, v."Name" asc; 

    -- 返回用户有权限的模块的列表
	select distinct 
        v.* 
    from 
        "waln_authority"."v_authentication_module" as v 
	join "waln_authority"."permissions" as p on p."ModuleId" = v."Id" and (p."ExpireTime" is null or p."ExpireTime" >= @now) and 
		((p."RoleType" = 4 and p."RoleId" = @userId) or 
		 (p."RoleType" = 3 and p."RoleId" in (select distinct "PostId" from "waln_authority"."userposts" where "UserId" = @userId and "PostId" is not null)) or 
		 (p."RoleType" = 2 and p."RoleId" in (select distinct "DeptId" from "waln_authority"."userposts" where "UserId" = @userId and "DeptId" is not null)) or 
         (p."RoleType" = 1 and p."RoleId" in (select distinct "OrgId" from "waln_authority"."userposts" where "UserId" = @userId and "OrgId" is not null)))
    where 
        v."Presentation" = 1
    order by 
        v."Position" asc; 

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Orgs - 根组织列表
 * 
 * -- 描述:
 *   查询系统中创建的所有根组织的列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_orgs"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_orgs" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_orgs"
	@userId uniqueidentifier, -- 用户 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 验证参数 @userId (用户 Id)
	if @userId is null
	begin
		set @_message = N'没有提供有效的 用户 Id。';
		return;
	end

	-- 返回 Orgs.list
	select
		v.*
	from
		"waln_authority"."v_organization_org" as v;

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   AddOrg - 添加组织
 * 
 * -- 描述:
 *   添加一个根组织机构。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_addorg"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_addorg" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_addorg"
	@userId uniqueidentifier, -- 用户 Id 
	@name nvarchar(50), -- 名称 
	@description nvarchar(2000), -- 描述 
	@createDate date, -- 设立日期 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	declare @id uniqueidentifier;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @name (名称)
    if @name is null or @name = ''
	begin
        set @_message = N'没有提供组织名称。';
        return;
    end

    -- 验证参数 @createDate (设立日期)
    set @createDate = cast(sysdatetime() as date);
    
    -- 检查名称是否存在
    if exists (select * from "waln_authority"."depts" where "ParentId" is null and "Name" = @name)
	begin
        set @_message = concat(N'名称为 ', @name, N' 的组织已经存在。');
        return;
    end
	
	set @id = newid();
    insert into "waln_authority"."depts" 
    (
        "Id", -- Id
        "Level", -- 层级 
        "ParentId", -- 上级部门的 Id 
        "OrgId", -- 根组织的 Id 
        "Name", -- 名称 
        "Description", -- 描述 
        "CreateDate"  -- 设立日期 
    )
    values
    (
        @id, -- Id
        0, -- 层级 
        null, -- 上级部门的 Id 
        @id, -- 根组织的 Id 
        @name, -- 名称 
        @description, -- 描述 
        @createDate  -- 设立日期 
    );

    -- 返回 AddOrg.Org
    select 
        v.* 
    from 
        "waln_authority"."v_organization_org" as v 
    where 
        v."Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'添加了一个新的组织：', @name, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.depts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_addorg', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   EditOrg - 设置组织信息
 * 
 * -- 描述:
 *   设置指定的组织机构的信息。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_editorg"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_editorg" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_editorg"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@name nvarchar(50), -- 名称 
	@description nvarchar(2000), -- 描述 
	@createDate date, -- 设立日期 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供组织的 Id。';
        return;
    end

    -- 验证参数 @name (名称)
    if @name is null or @name = '' 
	begin
        set @_message = N'没有提供名称。';
        return;
    end
    
    -- 检查名称是否存在
    if exists (select "PK" from "waln_authority"."depts" where "ParentId" is null and "Name" = @name and "Id" <> @id) 
	begin
        set @_message = concat(N'名称为 ', @name, N' 的组织已经存在。');
        return;
    end

    update 
        "waln_authority"."depts" 
    set 
        "Name" = @name, 
        "Description" = @description
    where 
        "Id" = @id; 

    -- 返回 EditOrg.Org
    select 
        v.* 
    from 
        "waln_authority"."v_organization_org" as v 
    where 
        v."Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'设置了组织：', @name, N' 的资料。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		4, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.depts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_editorg', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   DeleteOrg - 删除组织
 * 
 * -- 描述:
 *   删除一个指定的根组织，其中包含所有部门、岗位都会被删除，但是用户账号不会被删除。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_deleteorg"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_deleteorg" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_deleteorg"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	declare @name varchar(50) = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供组织的 Id。';
        return;
    end
    
    select @name = "Name" from "waln_authority"."depts" where "Id" = @id;

    if @name is not null 
	begin
		-- 删除组织直接相关的权限
		delete from "waln_authority"."permissions" where "RoleType" = 1 and "RoleId" = @id;
        
        -- 删除组织中部门相关的权限
		delete p from "waln_authority"."permissions" as p 
        join "waln_authority"."depts" as d on d."Id" = p."RoleId" and p."RoleType" = 2 
        where d."OrgId" = @id;
        
        -- 删除组织中岗位相关的权限
		delete p from "waln_authority"."permissions" as p 
        join "waln_authority"."posts" as t on t."Id" = p."RoleId" and p."RoleType" = 3 
        join "waln_authority"."depts" as d on d."Id" = t."DeptId" 
        where d."OrgId" = @id;
        
        -- 删除组织中的用户岗位
		delete from "waln_authority"."userposts" where "OrgId" = @id;
        
        -- 删除组织中的岗位以及组织中部门的岗位
        delete p from "waln_authority"."posts" as p
        join "waln_authority"."depts" as d on d."Id" = p."DeptId" 
        where d."OrgId" = @id;
        
        -- 删除组织自身
        delete from "waln_authority"."depts" where "OrgId" = @id;
    end

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'删除了组织：', @name, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		8, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.depts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_deleteorg', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   DeptChildren - 下级部门、岗位和用户
 * 
 * -- 描述:
 *   查询隶属于指定部门或组织的下级部门、工作岗位和用户的列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_deptchildren"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_deptchildren" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_deptchildren"
	@userId uniqueidentifier, -- 用户 Id 
	@deptId uniqueidentifier, -- 上级部门的 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    declare @level tinyint = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @deptId (上级部门的 Id)
    if @deptId is null 
	begin
        set @_message = N'没有提供上级部门的 Id。';
        return;
    end
    
    -- 组织或部门的层级
    select @level = "Level" from "waln_authority"."depts" where "Id" = @deptId;
    if @level is null 
	begin
        set @_message = N'没有找到组织或部门。';
        return;
    end
	
    -- 下级部门
    select 
        v.* 
    from 
        "waln_authority"."v_organization_dept" as v 
    where 
        v."ParentId" = @deptId; 
		
    -- 组织或部门中的岗位
    select 
        v.* 
    from 
        "waln_authority"."v_organization_post" as v 
    where 
        v."DeptId" = @deptId; 
		
    -- 组织或部门中的用户
    if @level > 0 
	begin
		-- 部门中的用户
		select 
			v.* 
		from 
			"waln_authority"."v_organization_user" as v 
		where 
			v."DeptId" = @deptId and v."PostId" is null; 
	end
    else
	begin
		-- 组织中的用户
		select 
			v.* 
		from 
			"waln_authority"."v_organization_user" as v 
		where 
			v."OrgId" = @deptId and (v."DeptId" is null or v."DeptId" = v."OrgId") and v."PostId" is null; 
	end

    -- 指示操作成功
    set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   AddDept - 添加部门
 * 
 * -- 描述:
 *   为组织或一个上级部门添加一个下级部门。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_adddept"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_adddept" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_adddept"
	@userId uniqueidentifier, -- 用户 Id 
	@parentId uniqueidentifier, -- 上级部门的 Id 
	@name nvarchar(50), -- 名称 
	@description nvarchar(2000), -- 描述 
	@createDate date, -- 设立日期 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	-- 新部门的 Id
    declare @id uniqueidentifier = null;
	-- 隶属组织的 Id
    declare @orgId uniqueidentifier = null;
    -- 新部门的层级
    declare @level tinyint = null;

    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @parentId (上级部门的 Id)
    if @parentId is null 
	begin
        set @_message = N'没有提供上级部门的 Id。';
        return;
    end

    -- 验证参数 @name (名称)
    if @name is null or @name = '' 
	begin
        set @_message = N'没有提供名称。';
        return;
    end
    
    -- 查询组织的 Id 和上级部门的层级
    select @orgId = "OrgId", @level = "Level" from "waln_authority"."depts" where "Id" = @parentId;
    if @orgId is null 
	begin
        set @_message = N'没有找到上级部门。';
        return;
    end
    
    -- 组织的层级为 0，因此包含组织在内，最大支持 8 级
    if @level >= 7 
	begin
        set @_message = N'已经达到最大支持的层级，不能再添加新的部门。';
        return;
    end
    
    -- 验证参数 @createDate (设立日期)
    set @createDate = cast(sysdatetime() as date);
    
    -- 检查名称是否存在
    if exists (select "PK" from "waln_authority"."depts" where "ParentId" = @parentId and "Name" = @name) 
	begin
        set @_message = concat(N'名称为 ', @name, N' 的部门已经存在。');
        return;
    end

	set @id = newid();
    insert into "waln_authority"."depts" 
    (
        "Id", -- Id
        "Level", -- 层级 
        "ParentId", -- 上级部门的 Id 
        "OrgId", -- 根组织的 Id 
        "Name", -- 名称 
        "Description", -- 描述 
        "CreateDate"  -- 设立日期 
    )
    values
    (
        @id, -- Id
        @level + 1, -- 层级 
        @parentId, -- 上级部门的 Id 
        @orgId, -- 根组织的 Id 
        @name, -- 名称 
        @description, -- 描述 
        @createDate  -- 设立日期 
    );

    -- 返回 AddDept.Dept
    select 
        v.* 
    from 
        "waln_authority"."v_organization_dept" as v 
    where 
        v."Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'添加了新的部门：', @name, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.depts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_adddept', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   EditDept - 设置部门资料
 * 
 * -- 描述:
 *   设置指定部门的资料信息。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_editdept"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_editdept" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_editdept"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@name nvarchar(50), -- 名称 
	@description nvarchar(2000), -- 描述 
	@createDate date, -- 设立日期 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
    declare @parentId uniqueidentifier = null; -- 上级部门 Id

    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供部门的 Id。';
        return;
    end

    -- 验证参数 @name (名称)
    if @name is null or @name = '' 
	begin
        set @_message = N'没有提供名称。';
        return;
    end
    
	-- 查找上级部门的 Id
    select @parentId = "ParentId" from "waln_authority"."depts" where "Id" = @id;
    if @parentId is null 
	begin
        set @_message = N'没有提供目标部门。';
        return;
    end
    
    -- 检查名称是否已经存在
    if exists (select "PK" from "waln_authority"."depts" where "ParentId" = @parentId and "Name" = @name and "Id" <> @id) 
	begin
        set @_message = concat(N'名称为 ', @name, N' 的部门已经存在。');
        return;
    end

    update 
        "waln_authority"."depts" 
    set 
        "Name" = @name, 
        "Description" = @description
    where 
        "Id" = @id; 

    -- 返回 EditDept.Dept
    select 
        v.* 
    from 
        "waln_authority"."v_organization_dept" as v 
    where 
        v."Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'设置了部门：', @name, N' 的资料。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		4, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.depts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_editdept', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   DeleteDept - 删除部门
 * 
 * -- 描述:
 *   删除一个指定的部门，隶属其中的下级部门和工作岗位都将被删除，但是用户账号不会被删除。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_deletedept"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_deletedept" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_deletedept"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;

	-- 部门名称
	declare @name varchar(50) = null;
	-- 部门层级
	declare @level tinyint = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供Id。';
        return;
    end
    
	-- 查找要删除部门的名称和层级
    select @name = "Name", @level = "Level" from "waln_authority"."depts" where "Id" = @id;
    
    if @name is null 
	begin
        set @_message = N'没有找到目标部门。';
        return;
    end

	-- 递归删除部门
    exec "waln_authority"."_delete_dept" @id, @level;

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'删除了部门：', @name, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		8, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.depts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_deletedept', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   AddPost - 添加工作岗位
 * 
 * -- 描述:
 *   为指定的组织或部门添加一个工作岗位。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_addpost"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_addpost" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_addpost"
	@userId uniqueidentifier, -- 用户 Id 
	@deptId uniqueidentifier, -- 隶属部门的 Id 
	@name nvarchar(50), -- 岗位名称 
	@description nvarchar(2000), -- 描述 
	@createDate date, -- 设立日期 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	-- 新岗位的 Id
	declare @id uniqueidentifier;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @deptId (隶属部门的 Id)
    if @deptId is null 
	begin
        set @_message = N'没有提供隶属部门的 Id。';
        return;
    end

    -- 验证参数 @name (岗位名称)
    if @name is null or @name = '' 
	begin
        set @_message = N'没有提供岗位名称。';
        return;
    end
    
    -- 检查名称是否已经存在
    if exists (select "PK" from "waln_authority"."posts" where "DeptId" = @deptId and "Name" = @name) 
	begin
        set @_message = concat(N'名称为 ', @name, N' 的岗位已经存在。');
        return;
    end

    -- 验证参数 @createDate (设立日期)
    set @createDate = cast(sysdatetime() as date);

	set @id = newid();
    insert into "waln_authority"."posts"
    (
		"Id",
        "DeptId", 
        "Name", 
        "Description", 
        "CreateDate"
    )
    values
    (
		@id,
        @deptId, 
        @name, 
        @description, 
        @createDate
    );

    -- 返回 AddPost.Post
    select 
        v.* 
    from 
        "waln_authority"."v_organization_post" as v 
    where 
        v."Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'添加了新的工作岗位：', @name, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.posts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_addpost', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   EditPost - 设置工作岗位
 * 
 * -- 描述:
 *   设置指定工作岗位的信息。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_editpost"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_editpost" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_editpost"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@name nvarchar(50), -- 岗位名称 
	@description nvarchar(2000), -- 描述 
	@createDate date, -- 设立日期 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
    declare @deptId uniqueidentifier = null; -- 隶属部门的 Id

    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供岗位的 Id。';
        return;
    end

    -- 验证参数 @name (岗位名称)
    if @name is null or @name = '' 
	begin
        set @_message = N'没有提供岗位名称。';
        return;
    end
    
	-- 隶属的部门或组织
    select @deptId = "DeptId" from "waln_authority"."posts" where "Id" = @id;
    if @deptId is null 
	begin
        set @_message = N'没有提供目标岗位。';
        return;
    end
    
    -- 检查名称是否已经存在
    if exists (select "PK" from "waln_authority"."posts" where "DeptId" = @deptId and "Name" = @name and "Id" <> @id) 
	begin
        set @_message = concat(N'名称为 ', @name, N' 的岗位已经存在。');
        return;
    end

    update 
        "waln_authority"."posts" 
    set 
        "Name" = @name, 
        "Description" = @description
    where 
        "Id" = @id; 

    -- 返回 EditPost.Post
    select 
        v.* 
    from 
        "waln_authority"."v_organization_post" as v 
    where 
        v."Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'设置了工作岗位：', @name, N' 的资料。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		4, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.posts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_editpost', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   DeletePost - 删除工作岗位
 * 
 * -- 描述:
 *   删除一个工作岗位，不会删除属于该工作岗位的用户账号。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_deletepost"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_deletepost" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_deletepost"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	-- 要删除岗位的名称
	declare @name varchar(50) = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供岗位的 Id。';
        return;
    end
    
    -- 岗位名称
    select @name = "Name" from "waln_authority"."posts" where "Id" = @id;
    
    if @name is null 
	begin
        set @_message = N'没有找到目标岗位。';
        return;
    end
    
    -- 删除岗位直接对应的权限
    delete from "waln_authority"."permissions" where "RoleType" = 3 and "RoleId" = @id;
    -- 删除岗位中的用户
    delete from "waln_authority"."userposts" where "PostId" = @id;
    -- 删除岗位自身
    delete from "waln_authority"."posts" where "Id" = @id;

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'删除了工作岗位：', @name, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		8, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.posts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_deletepost', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   NewUser - 添加新用户
 * 
 * -- 描述:
 *   添加一个新的用户账号。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_newuser"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_newuser" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_newuser"
	@userId uniqueidentifier, -- 用户 Id 
	@loginName nvarchar(50), -- 登录名称 
	@password nchar(30), -- 登录密码明文 
	@no varchar(20), -- 编号 
	@name nvarchar(50), -- 用户名称 
	@mobile varchar(20), -- 手机号码 
	@email varchar(50), -- 电子邮件地址 
	@loginHomeUrl nvarchar(200), -- 登录后首页 
	@loginHomeDescription nvarchar(100), -- 登录后首页说明 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;

	-- 新用户的 Id
    declare @id uniqueidentifier = null;
	-- 当前日期时间
    declare @now datetime2 = sysdatetime();

    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @loginName (登录名称)
    if @loginName is null or @loginName = '' 
	begin
        set @_message = N'没有提供登录名称。';
        return;
    end

    -- 验证参数 @password (登录密码明文)
    if @password is null or @password = '' or len(@password) < 6 
	begin
        set @_message = N'没有提供有效的登录密码。';
        return;
    end
    
    -- 检查登录名称是否已经被注册
    if exists (select "PK" from "waln_authority"."users" where "LoginName" = @loginName) 
	begin
        set @_message = concat(N'登录名称 ', @loginName, N' 已经被注册。');
        return;
    end

    -- 验证参数 @name (用户名称)
    if @name is null or @name = '' 
	begin
        set @name = @loginName;
    end

    -- 验证参数 @no (编号)
    if @no is not null and @no <> '' 
	begin
		-- 检查手机号码是否已经被注册
		if exists (select "PK" from "waln_authority"."users" where "no" = @no) 
		begin
			set @_message = concat(N'用户编号 ', @no, N' 已经被注册。');
			return;
		end
	else
		set @no = null;
    end

    -- 验证参数 @mobile (手机号码)
    if @mobile is not null and @mobile <> '' 
	begin
		-- 检查手机号码是否已经被注册
		if exists (select "PK" from "waln_authority"."users" where "Mobile" = @mobile) 
		begin
			set @_message = concat(N'手机号码 ', @mobile, N' 已经被注册。');
			return;
		end
	else
		set @mobile = null;
    end

    -- 验证参数 @email (电子邮件地址)
    if @email is not null and @email <> '' 
	begin
		-- 检查手机号码是否已经被注册
		if exists (select "PK" from "waln_authority"."users" where "Email" = @email) 
		begin
			set @_message = concat(N'电子邮件 ', @email, N' 已经被注册。');
			return;
		end
	else
		set @email = null;
    end

	set @id = newid();
    insert into "waln_authority"."users" 
    (
        "Id", -- Id
        "LoginName", -- 登录名称 
        "Password", -- 登录密码密文 
        "No", -- 编号 
        "Name", -- 用户名称 
        "FaceUrl", -- 头像路径 
        "Mobile", -- 手机号码 
        "Email", -- 电子邮件地址 
        "CreateTime", -- 创建时间 
        "LockTime", -- 锁定时间 
        "LoginTime", -- 登录时间 
        "Token", -- 用户令牌 
        "TokenExpireTime", -- 令牌有效期
        "LoginHomeUrl", 
        "LoginHomeDescription"
    )
    values
    (
        @id, -- Id
        @loginName, -- 登录名称 
        "waln_authority"."encrypt_password"(@password), -- 登录密码密文 
        @no, -- 编号 
        @name, -- 用户名称 
        null, -- 头像路径 
        @mobile, -- 手机号码 
        @email, -- 电子邮件地址 
        @now, -- 创建时间 
        null, -- 锁定时间 
        null, -- 登录时间 
        null, -- 用户令牌 
        null, -- 令牌有效期
        @loginHomeUrl, 
        @loginHomeDescription 
    );
    
    -- 返回 Add.User
    select distinct 
        v.* 
    from 
        "waln_authority"."v_organization_user" as v 
    where 
        v."UserId" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'添加了新的用户账号：', @name, N' (', @loginName, N')', N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.users', -- 相关数据库表的名称列表
		'waln_authority.p_organization_newuser', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   SearchDeptUsers - 搜索用户
 * 
 * -- 描述:
 *   按用户名、登录名、编号、手机或邮件搜索用户列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_searchdeptusers"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_searchdeptusers" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_searchdeptusers"
	@pageIndex bigint, -- 页码 
	@pageSize bigint, -- 页面尺寸 
	@totalRows bigint out, -- 总行数 
	@orderName nvarchar(30), -- 排序名称 
	@orderDirection nvarchar(4), -- 排序方向 
	@keyword nvarchar(50), -- 关键词 
	@deptId uniqueidentifier, -- 隶属部门的 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    set @_success = 0;
    set @_message = N'';
    
    if @deptId is null 
	begin
        set @_message = N'没有提供有效的部门或组织的 Id。';
        return;
    end

    -- 初始化 out 参数
    set @totalRows = 0; -- 总行数

    -- 验证参数 @pageIndex (页码)
    if @pageIndex is null or @pageIndex < 1 
	begin
        -- 默认分页查询的页码，从 1 开始
        set @pageIndex = 1;
    end

    -- 验证参数 @pageSize (页面尺寸)
    if @pageSize is null or @pageSize < 1 
	begin
        -- 默认分页查询的页面尺寸
        set @pageSize = 20;
    end

    -- 验证参数 @orderName (排序名称)
    if @orderName is null or @orderName = '' or @orderName not in 
    (
        -- 确保排序名称是以下名称之一，防止 SQL 注入式攻击
        'Name',
        'LoginName',
        'No',
        'Mobile',
        'Email',
        'CreateTime',
        'LoginTime'
    ) 
	begin
        -- 默认排序列的标识符
        set @orderName = 'Name';
    end

    -- 验证参数 @orderDirection (排序方向)
    if @orderDirection is null or @orderDirection not in ('asc', 'desc') 
	begin
        -- 默认排序方向
        set @orderDirection = 'asc';
    end
	
	-- 为 like 运算转义字符串参数 @keyword
	set @keyword = "waln_authority"."like_escape"(@keyword);

    -- 参数 @totalRows (总行数)
	select 
		@totalRows = count_big(*) 
	from 
		"waln_authority"."users" as v 
	where 
		(v."Id" not in (select distinct "UserId" from "waln_authority"."userposts" where "DeptId" = @deptId and "PostId" is null)) and 
		((@keyword is null) or (v."LoginName" like @keyword) or (v.No like @keyword) or (v."Name" like @keyword) or (v."Mobile" like @keyword) or (v."Email" like @keyword)); 

	-- 没有满足条件的记录行
	if @totalRows is null or @totalRows < 1
	begin
		set @totalRows = 0;
		-- 指示操作成功
		set @_success = 1;
		return;
	end

	-- 动态查询命令文本
	declare @_page_sql nvarchar(4000);
	-- 动态查询命令参数定义文本
	declare @_params_def nvarchar(4000);
	-- 分页用的起始行号，从 1 开始
	declare @_start_row bigint;
	-- 分页用的截至行号，从 1 开始
	declare @_end_row bigint;

	-- 根据页码和页面尺寸计算起始行号
	set @_start_row = 1;
	if @pageIndex > 1
	begin
		set @_start_row = (@pageIndex - 1) * @pageSize + 1;
	end

	-- 根据页面尺寸和起始行号计算截至行号
	set @_end_row = @_start_row + @pageSize - 1

	-- 动态创建查询命令文本
	set @_page_sql = 
		'select ' + 
			'__pv.* ' + 
		'from ' + 
		'(' + 
			'select ' + 
				'v.*, v."Id" as "UserId", ' + 
				'(row_number() over(order by ' + @orderName + ' ' + @orderDirection + ')) as __row_no ' + 
			'from ' + 
			'"waln_authority"."users" as v '+ 
			'where ' + 
				'(v."Id" not in (select distinct "UserId" from "waln_authority"."userposts" where "DeptId" = @deptId and "PostId" is null)) and ' + 
				'((@keyword is null) or (v."LoginName" like @keyword) or (v."Name" like @keyword) or (v."Mobile" like @keyword) or (v."Email" like @keyword)) ' + 
		') as __pv ' + 
		'where ' + 
			'__row_no >= ' + cast(@_start_row as varchar(20)) + ' and __row_no <= ' + cast(@_end_row as varchar(20));

	set @_params_def = 
		'@keyword nvarchar(50),' + 
		'@deptId uniqueidentifier'; 

	exec "dbo"."sp_executesql" @_page_sql, @_params_def, 
		@keyword, 
		@deptId;

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   AddDeptUsers - 添加部门用户
 * 
 * -- 描述:
 *   为指定的部门添加一个或多个用户账号。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_adddeptusers"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_adddeptusers" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_adddeptusers"
	@userId uniqueidentifier, -- 用户 Id 
	@deptId uniqueidentifier, -- 隶属部门的 Id 
	@ids nvarchar(1000), -- 目标用户的 Id 的列表，多个之间使用 ',' 分隔 
	@description nvarchar(2000), -- 描述 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	
	-- 隶属组织的 Id
    declare @orgId uniqueidentifier = null;
    -- 部门的名称
    declare @name varchar(50) = null;
    -- 当前日期时间
    declare @now datetime2 = sysdatetime();
	-- 实际添加的用户数目
	declare @rowCount bigint = 0;

    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @deptId (隶属部门的 Id)
    if @deptId is null 
	begin
        set @_message = N'没有提供隶属部门的 Id。';
        return;
    end

    -- 验证参数 @ids (目标用户的 Id 的列表)
    if @ids is null or @ids = '' 
	begin
        set @_message = N'没有提供目标用户的 Id 的列表。';
        return;
    end
    
	-- 部门隶属组织和名称
    select @orgId = "OrgId", @name = "Name" from "waln_authority"."depts" where "Id" = @deptId;
    if @orgId is null 
	begin
        set @_message = N'没有找到目标部门或组织。';
        return;
    end

    insert into "waln_authority"."userposts"
    (
		"UserId",
        "PostId",
        "DeptId",
        "OrgId",
		"CreateTime",
        "Description"
    )
    select distinct
		"Id",
        null,
        @deptId,
        @orgId,
        @now,
        case when @description = '' then null else @description end
    from
		"waln_authority"."users"
	where 
		charindex("waln_authority"."uuid_to_b64"("Id") + ',', @ids + ',') > 0 and 
		"Id" not in (select distinct "UserId" from "waln_authority"."userposts" where "DeptId" = @deptId and "PostId" is null);

	set @rowCount = rowcount_big();
    
    if @rowCount < 1 
	begin
		set @_success = 1;
        return;
    end

    -- 返回 AddDeptUsers.Users
    select 
        v.* 
    from 
        "waln_authority"."v_organization_user" as v 
    where 
		v."DeptId" = @deptId and v."PostId" is null and charindex("waln_authority"."uuid_to_b64"(v."UserId") + ',', @ids + ',') > 0; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'为部门：', @name, N' 添加了 ', @rowCount, N' 个用户。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.userposts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_adddeptusers', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   PostUsers - 工作岗位用户列表
 * 
 * -- 描述:
 *   查询隶属于指定工作岗位的用户的列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_postusers"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_postusers" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_postusers"
	@userId uniqueidentifier, -- 用户 Id 
	@postId uniqueidentifier, -- 工作岗位的 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @postId (工作岗位的 Id)
    if @postId is null 
	begin
        set @_message = N'没有提供工作岗位的 Id。';
        return;
    end

    -- 返回 PostUsers.List
    select 
        v.* 
    from 
        "waln_authority"."v_organization_user" as v 
    where 
        v."PostId" = @postId; 

    -- 指示操作成功
    set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   SearchPostUsers - 搜索岗位用户
 * 
 * -- 描述:
 *   按用户名、登录名、编号、手机或邮件搜索用户列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_searchpostusers"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_searchpostusers" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_searchpostusers"
	@pageIndex bigint, -- 页码 
	@pageSize bigint, -- 页面尺寸 
	@totalRows bigint out, -- 总行数 
	@orderName nvarchar(30), -- 排序名称 
	@orderDirection nvarchar(4), -- 排序方向 
	@keyword nvarchar(50), -- 关键词 
	@postId uniqueidentifier, -- 工作岗位的 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    set @_success = 0;
    set @_message = N'';
    
    if @postId is null 
	begin
        set @_message = N'没有提供有效的工作岗位的 Id。';
        return;
    end

    -- 初始化 out 参数
    set @totalRows = 0; -- 总行数

    -- 验证参数 @pageIndex (页码)
    if @pageIndex is null or @pageIndex < 1 
	begin
        -- 默认分页查询的页码，从 1 开始
        set @pageIndex = 1;
    end

    -- 验证参数 @pageSize (页面尺寸)
    if @pageSize is null or @pageSize < 1 
	begin
        -- 默认分页查询的页面尺寸
        set @pageSize = 20;
    end

    -- 验证参数 @orderName (排序名称)
    if @orderName is null or @orderName = '' or @orderName not in 
    (
        -- 确保排序名称是以下名称之一，防止 SQL 注入式攻击
        'Name',
        'LoginName',
        'No',
        'Mobile',
        'Email',
        'CreateTime',
        'LoginTime'
    ) 
	begin
        -- 默认排序列的标识符
        set @orderName = 'Name';
    end

    -- 验证参数 @orderDirection (排序方向)
    if @orderDirection is null or @orderDirection not in ('asc', 'desc') 
	begin
        -- 默认排序方向
        set @orderDirection = 'asc';
    end

    -- 为 like 运算转义字符串参数 @keyword
	set @keyword = "waln_authority"."like_escape"(@keyword);

	-- 参数 @totalRows (总行数)
	select 
		@totalRows = count_big(*) 
	from 
		"waln_authority"."users" as v 
	where 
		v."Id" not in (select distinct "UserId" from "waln_authority"."userposts" where "PostId" = @postId) and 
		((@keyword is null) or (v."LoginName" like @keyword) or (v.No like @keyword) or (v."Name" like @keyword) or (v."Mobile" like @keyword) or (v."Email" like @keyword)); 

	-- 没有满足条件的记录行
	if @totalRows is null or @totalRows < 1
	begin
		set @totalRows = 0;
		-- 指示操作成功
		set @_success = 1;
		return;
	end

	-- 动态查询命令文本
	declare @_page_sql nvarchar(4000);
	-- 动态查询命令参数定义文本
	declare @_params_def nvarchar(4000);
	-- 分页用的起始行号，从 1 开始
	declare @_start_row bigint;
	-- 分页用的截至行号，从 1 开始
	declare @_end_row bigint;

	-- 根据页码和页面尺寸计算起始行号
	set @_start_row = 1;
	if @pageIndex > 1
	begin
		set @_start_row = (@pageIndex - 1) * @pageSize + 1;
	end

	-- 根据页面尺寸和起始行号计算截至行号
	set @_end_row = @_start_row + @pageSize - 1

	-- 动态创建查询命令文本
	set @_page_sql = 
		'select ' + 
			'__pv.* ' + 
		'from ' + 
		'(' + 
			'select ' + 
				'v.*, v."Id" as "UserId", ' + 
				'(row_number() over(order by ' + @orderName + ' ' + @orderDirection + ')) as __row_no ' + 
			'from ' + 
			'"waln_authority"."users" as v '+ 
			'where ' + 
				'v."Id" not in (select distinct "UserId" from "waln_authority"."userposts" where "PostId" = @postId) and ' + 
				'((@keyword is null) or (v."LoginName" like @keyword) or (v."Name" like @keyword) or (v."Mobile" like @keyword) or (v."Email" like @keyword)) ' + 
		') as __pv ' + 
		'where ' + 
			'__row_no >= ' + cast(@_start_row as varchar(20)) + ' and __row_no <= ' + cast(@_end_row as varchar(20));

	set @_params_def = 
		'@keyword nvarchar(50),' + 
		'@postId uniqueidentifier'; 

	exec "dbo"."sp_executesql" @_page_sql, @_params_def, 
		@keyword, 
		@postId;
    
    -- 指示操作成功
    set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   AddPostUsers - 添加岗位用户
 * 
 * -- 描述:
 *   为指定的岗位添加一个或多个用户。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_addpostusers"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_addpostusers" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_addpostusers"
	@userId uniqueidentifier, -- 用户 Id 
	@postId uniqueidentifier, -- 工作岗位的 Id 
	@ids nvarchar(1000), -- 目标用户的 Id 列表 
	@description nvarchar(2000), -- 描述 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;

	-- 隶属组织的 Id
    declare @orgId uniqueidentifier = null;
	-- 隶属部门的 Id
    declare @deptId uniqueidentifier = null;
	-- 岗位名称
    declare @name varchar(50) = null;
	-- 当前日期时间
    declare @now datetime2 = sysdatetime();
	-- 实际添加到岗位的用户数目
	declare @rowCount bigint = 0;

    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @postId (工作岗位的 Id)
    if @postId is null 
	begin
        set @_message = N'没有提供工作岗位的 Id。';
        return;
    end

    -- 验证参数 @ids (目标用户的 Id 列表)
    if @ids is null or @ids = '' 
	begin
        set @_message = N'没有提供目标用户的 Id 列表。';
        return;
    end
	
	-- 岗位隶属部门和名称
    select @deptId = "DeptId", @name = "Name" from "waln_authority"."posts" where "Id" = @postId;
    if @deptId is null 
	begin
        set @_message = N'没有找到目标部门或组织。';
        return;
    end

	-- 隶属组织
    select @orgId = "OrgId" from "waln_authority"."depts" where "Id" = @deptId;
    if @orgId is null 
	begin
        set @_message = N'没有找到目标组织。';
        return;
    end

    insert into "waln_authority"."userposts"
    (
		"UserId",
        "PostId",
        "DeptId",
        "OrgId",
		"CreateTime",
        "Description"
    )
    select distinct
		"Id",
        @postId,
        @deptId,
        @orgId,
        @now,
        case when @description = '' then null else @description end
    from
		"waln_authority"."users"
	where 
		charindex("waln_authority"."uuid_to_b64"("Id") + ',', @ids + ',') > 0 and 
		"Id" not in (select distinct "UserId" from "waln_authority"."userposts" where "PostId" = @postId);
        
	set @rowCount = rowcount_big();
    
    if @rowCount < 1 
	begin
		set @_success = 1;
		return;
    end

    -- 返回 AddPostUsers.Users
    select 
        v.* 
    from 
        "waln_authority"."v_organization_user" as v 
    where 
		v."PostId" = @postId and charindex("waln_authority"."uuid_to_b64"(v."UserId") + ',', @ids + ',') > 0; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'为岗位：', @name, N' 添加了 ', @rowCount, N' 个用户。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.userposts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_addpostusers', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   EditUser - 设置用户
 * 
 * -- 描述:
 *   设置指定岗位或部门中的一个用户的备注描述信息。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_edituser"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_edituser" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_edituser"
	@userId uniqueidentifier, -- 用户 Id 
	@pk bigint, -- 主键 
	@description nvarchar(2000), -- 描述 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @pk (主键)
    if @pk is null 
	begin
        set @_message = N'没有提供主键。';
        return;
    end

    update 
        "waln_authority"."userposts" 
    set 
        "Description" = @description 
    where 
        "PK" = @pk; 

    -- 返回 EditUser.Info
    select 
        v.* 
    from 
        "waln_authority"."v_organization_user" as v 
    where 
        v."PK" = @pk; 

    set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   RemoveUser - 移除用户
 * 
 * -- 描述:
 *   移除指定岗位或部门中的一个用户。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_removeuser"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_removeuser" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_removeuser"
	@userId uniqueidentifier, -- 用户 Id 
	@pk bigint, -- 主键 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;

	-- 组织名称
	declare @orgName varchar(50) = null;
	-- 部门名称
	declare @deptName varchar(50) = null;
	-- 岗位名称
	declare @postName varchar(50) = null;
	-- 用户名称
	declare @userName varchar(120) = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @pk (主键)
    if @pk is null 
	begin
        set @_message = N'没有提供主键。';
        return;
    end
    
    -- 查询用户名称、岗位名称、部门名称和组织名称
    select @orgName = o."Name", @postName = p."Name", @deptName = d."Name", @userName = concat(u."Name", N' (', u."LoginName", N')')
    from "waln_authority"."userposts" as up
    left join "waln_authority"."posts" as p on p."Id" = up."PostId"
    left join "waln_authority"."depts" as d on d."Id" = up."DeptId"
    join "waln_authority"."depts" as o on o."Id" = up."OrgId"
    join "waln_authority"."users" as u on u."Id" = up."UserId"
    where up."PK" = @pk;
    
	-- 移除用户，不是删除用户记录
    delete from 
        "waln_authority"."userposts" 
    where 
        "PK" = @pk; 

    set @_success = 1;
    
	-- 记录操作日志
    if @postName is not null
		set @_log_body = concat(N'为岗位：', @postName, N' 移除了一个用户： ', @userName, N'。');
    else if @deptName is not null
		set @_log_body = concat(N'为部门：', @deptName, N' 移除了一个用户： ', @userName, N'。');
    else
		set @_log_body = concat(N'为组织：', @orgName, N' 移除了一个用户： ', @userName, N'。');

	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		8, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.userposts', -- 相关数据库表的名称列表
		'waln_authority.p_organization_removeuser', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   Apps - App 列表
 * 
 * -- 描述:
 *   查询已经安装的所有 App 的列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_apps"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_apps" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_apps"
	@userId uniqueidentifier, -- 用户 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 返回 Apps.List
    select 
        v.* 
    from 
        "waln_authority"."v_organization_app" as v; 

    -- 指示操作成功
    set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Modules - 模块列表
 * 
 * -- 描述:
 *   查询隶属于指定 App 的模块的列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_modules"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_modules" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_modules"
	@userId uniqueidentifier, -- 用户 Id 
	@appId uniqueidentifier, -- 隶属 App 的 Id 
	@roleType tinyint, -- 权利人类型代码 
	@roleId uniqueidentifier, -- 权利对象的 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @appId (隶属 App 的 Id)
    if @appId is null 
	begin
        set @_message = N'没有提供隶属 App 的 Id。';
        return;
    end

    -- 验证参数 @roleType (权利人类型代码)
    if @roleType is null 
	begin
        set @_message = N'没有提供权利人类型代码。';
        return;
    end

    -- 验证参数 @roleId (权利对象的 Id)
    if @roleId is null 
	begin
        set @_message = N'没有提供权利对象的 Id。';
        return;
    end

    -- 返回 Modules.List
    select 
        v.*,
        -- 对于身份验证和用户资料，总是认为是授权的
        (case when v."Id" in ('11E628C9-C5D9-5C9E-8836-A45E60DCC700' /* 身份验证 */, '11E63583-FEC6-0AF0-A936-A45E60DCC700' /* 用户资料 */) then cast('2016-01-01' as datetime2) else p."GrantTime" end) as "GrantTime",
        p."ExpireTime"
    from 
        "waln_authority"."v_organization_module" as v 
	left join
		"waln_authority"."permissions" as p on p."ModuleId" = v."Id" and p."RoleType" = @roleType and p."RoleId" = @roleId
    where 
        v."AppId" = @appId; 

    -- 指示操作成功
    set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Grant - 授权
 * 
 * -- 描述:
 *   为指定的部门、岗位或用户添加一个权限。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_grant"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_grant" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_grant"
	@userId uniqueidentifier, -- 用户 Id 
	@moduleId uniqueidentifier, -- 模块的 Id 
	@roleId uniqueidentifier, -- 权利对象的 Id 
	@roleType tinyint, -- 权利人类型代码 
	@expireTime datetime2, -- 有效截止时间 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	
	-- 授权用户、岗位、部门或组织的名称
	declare @roleName varchar(50) = null;
    -- 要授权模块隶属的应用
    declare @appId uniqueidentifier = null;
    -- 应用名称
    declare @appName varchar(50) = null;
    -- 模块名称
    declare @moduleName varchar(50) = null;
    -- 用户、岗位、部门或组织的类型名称
    declare @roleTypeName varchar(10) = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @moduleId (模块的 Id)
    if @moduleId is null 
	begin
        set @_message = N'没有提供模块的 Id。';
        return;
    end

    -- 验证参数 @roleId (权利对象的 Id)
    if @roleId is null 
	begin
        set @_message = N'没有提供权利对象的 Id。';
        return;
    end

    -- 验证参数 @roleType (权利人类型代码)：1-组织，2-部门，3-岗位，4-用户
    if @roleType is null or @roleType not in (1, 2, 3, 4)
	begin
        set @_message = N'没有提供权利人类型代码。';
        return;
    end
    
    if @roleType = 1 or @roleType = 2 
	begin
		-- 组织或部门
		select @roleName = "Name"
		from "waln_authority"."depts"
		where "Id" = @roleId;
        
        set @roleTypeName = case when @roleType = 1 then N'组织' else N'部门' end;
        
		if @roleName is null 
		begin
			set @_message = N'没有找到目标组织或部门。';
			return;
		end
	end
    else if @roleType = 3 
	begin
		-- 岗位
		select @roleName = "Name"
		from "waln_authority"."posts"
		where "Id" = @roleId;
        
		set @roleTypeName = N'岗位';
        
		if @roleName is null 
		begin
			set @_message = N'没有找到目标岗位。';
			return;
		end
	end
	else
	begin
		-- 用户
		select @roleName = concat("Name", N' (', "LoginName", N')')
		from "waln_authority"."users"
		where "Id" = @roleId;
        
        set @roleTypeName = N'用户';
        
		if @roleName is null 
		begin
			set @_message = N'没有找到目标用户。';
			return;
		end
    end
    
	-- 隶属应用和模块名称
    select @appId = "AppId", @moduleName = "Name"
    from "waln_authority"."modules" 
    where "Id" = @moduleId;
    
    if @appId is null 
	begin
        set @_message = N'没有找到目标模块。';
        return;
    end
    
    -- 应用名称
    select @appName = "Name"
    from "waln_authority"."apps"
    where "Id" = @appId;

	-- 身份验证和用户资料不需要授权，所有用户默认总是具有权限
    if @moduleId not in ('11E628C9-C5D9-5C9E-8836-A45E60DCC700' /* 身份验证 */, '11E63583-FEC6-0AF0-A936-A45E60DCC700' /* 用户资料 */) and
		not exists (select * from "waln_authority"."permissions" where "ModuleId" = @moduleId and "RoleId" = @roleId and "RoleType" = @roleType) 
	begin
		insert into "waln_authority"."permissions" 
		(
			"ModuleId", -- 模块的 Id 
			"RoleId", -- 权利对象的 Id 
			"RoleType", -- 权利人类型代码 
			"GrantTime", -- 授权时间 
			"ExpireTime"  -- 有效截止时间 
		)
		values
		(
			@moduleId, -- 模块的 Id 
			@roleId, -- 权利对象的 Id 
			@roleType, -- 权利人类型代码 
			sysdatetime(), -- 授权时间 
			null  -- 有效截止时间 
		);
    end
    
    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'为', @roleTypeName, N'： ', @roleName, N' 分配了新的权限，目标模块为：', @appName, N'.', @moduleName, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.permissions', -- 相关数据库表的名称列表
		'waln_authority.p_organization_grant', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   Revoke - 取消授权
 * 
 * -- 描述:
 *   删除一个权限。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_organization_revoke"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_organization_revoke" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_organization_revoke"
	@userId uniqueidentifier, -- 用户 Id 
	@moduleId uniqueidentifier, -- 模块的 Id 
	@roleId uniqueidentifier, -- 权利对象的 Id 
	@roleType tinyint, -- 权利人类型代码 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	
	-- 授权用户、岗位、部门或组织的名称
	declare @roleName varchar(50) = null;
    -- 要授权模块隶属的应用
    declare @appId uniqueidentifier = null;
    -- 应用名称
    declare @appName varchar(50) = null;
    -- 模块名称
    declare @moduleName varchar(50) = null;
    -- 用户、岗位、部门或组织的类型名称
    declare @roleTypeName varchar(10) = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @moduleId (模块的 Id)
    if @moduleId is null 
	begin
        set @_message = N'没有提供模块的 Id。';
        return;
    end

    -- 验证参数 @roleId (权利对象的 Id)
    if @roleId is null 
	begin
        set @_message = N'没有提供权利对象的 Id。';
        return;
    end

    -- 验证参数 @roleType (权利人类型代码)：1-组织，2-部门，3-岗位，4-用户
    if @roleType is null or @roleType not in (1, 2, 3, 4)
	begin
        set @_message = N'没有提供权利人类型代码。';
        return;
    end

    if @roleType = 1 or @roleType = 2 
	begin
		-- 组织或部门
		select @roleName = "Name"
		from "waln_authority"."depts"
		where "Id" = @roleId;
        
        set @roleTypeName = case when @roleType = 1 then N'组织' else N'部门' end;
        
		if @roleName is null 
		begin
			set @_message = N'没有找到目标组织或部门。';
			return;
		end
	end
    else if @roleType = 3 
	begin
		-- 岗位
		select @roleName = "Name"
		from "waln_authority"."posts"
		where "Id" = @roleId;
        
        set @roleTypeName = N'岗位';
        
		if @roleName is null 
		begin
			set @_message = N'没有找到目标岗位。';
			return;
		end
	end
	else
	begin
		-- 用户
		select @roleName = concat("Name", N' (', "LoginName", N')')
		from "waln_authority"."users"
		where "Id" = @roleId;
        
        set @roleTypeName = N'用户';
        
		if @roleName is null 
		begin
			set @_message = N'没有找到目标用户。';
			return;
		end
    end
    
	-- 隶属应用和模块名称
    select @appId = "AppId", @moduleName = "Name"
    from "waln_authority"."modules" 
    where "Id" = @moduleId;
    
    if @appId is null 
	begin
        set @_message = N'没有找到目标模块。';
        return;
    end
    
	-- 应用名称
    select @appName = "Name"
    from "waln_authority"."apps"
    where "Id" = @appId;
	
	-- 身份验证和用户资料不能倍撤销权限，所有用户总是对它们具有权限
	if @moduleId not in ('11E628C9-C5D9-5C9E-8836-A45E60DCC700' /* 身份验证 */, '11E63583-FEC6-0AF0-A936-A45E60DCC700' /* 用户资料 */) 
	begin
		delete from 
			"waln_authority"."permissions" 
		where 
			"ModuleId" = @moduleId and
			"RoleId" = @roleId and
			"RoleType" = @roleType; 
	end

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'为', @roleTypeName, N'： ', @roleName, N' 撤销了权限，目标模块为：', @appName, N'.', @moduleName, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		8, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.permissions', -- 相关数据库表的名称列表
		'waln_authority.p_organization_revoke', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   List - 用户列表
 * 
 * # 描述:
 *   查询并显示系统中已经注册的所有用户。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_list"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_list" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_list"
	@pageIndex bigint, -- 页码 
	@pageSize bigint, -- 页面尺寸 
	@totalRows bigint out, -- 总行数 
	@orderName nvarchar(30), -- 排序名称 
	@orderDirection nvarchar(4), -- 排序方向 
	@userId uniqueidentifier, -- 用户 Id 
	@name nvarchar(50), -- 用户名称 
	@locked bit, -- 已锁定用户 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 初始化 out 参数 @totalRows (总行数)
	set @totalRows = 0;

	-- 验证参数 @pageIndex (页码)
	if @pageIndex is null or @pageIndex < 1
	begin
		-- 默认分页查询的页码，从 1 开始
		set @pageIndex = 1;
	end

	-- 验证参数 @pageSize (页面尺寸)
	if @pageSize is null or @pageSize < 1
	begin
		-- 默认分页查询的页面尺寸
		set @pageSize = 20;
	end

	-- 验证参数 @orderName (排序名称)
	if @orderName is null or @orderName = '' or @orderName not in 
	(
		-- 确保排序名称是以下名称之一，防止 SQL 注入式攻击
        'Name',
        'LoginName',
        'No',
        'Mobile',
        'Email',
        'CreateTime',
        'LoginTime'
	)
	begin
		-- 默认排序列
		set @orderName = 'Name';
	end

	-- 验证参数 @orderDirection (排序方向)
	if @orderDirection is null or @orderDirection not in ('asc', 'desc')
	begin
		-- 默认排序方向
		set @orderDirection = 'asc';
	end

	-- 验证参数 @userId (用户 Id)
	if @userId is null
	begin
		set @_message = N'没有提供有效的 用户 Id。';
		return;
	end

	-- 返回 List.users
	-- 为 like 运算转义字符串参数 @name
	set @name = "waln_authority"."like_escape"(@name);

	-- 参数 @totalRows (总行数)
	select 
		@totalRows = count_big(*) 
	from 
		"waln_authority"."v_users_user" as v 
	where 
		((@name is null) or (v."LoginName" like @name) or (v."Name" like @name) or (v."Mobile" like @name) or (v."Email" like @name)) and 
		((@locked is null) or (@locked = 0 and v."LockTime" is null) or (@locked = 1 and v."LockTime" is not null)); 

	-- 没有满足条件的记录行
	if @totalRows is null or @totalRows < 1
	begin
		set @totalRows = 0;
		-- 指示操作成功
		set @_success = 1;
		return;
	end

	-- 动态查询命令文本
	declare @_page_sql nvarchar(4000);
	-- 动态查询命令参数定义文本
	declare @_params_def nvarchar(4000);
	-- 分页用的起始行号，从 1 开始
	declare @_start_row bigint;
	-- 分页用的截至行号，从 1 开始
	declare @_end_row bigint;

	-- 根据页码和页面尺寸计算起始行号
	set @_start_row = 1;
	if @pageIndex > 1
	begin
		set @_start_row = (@pageIndex - 1) * @pageSize + 1;
	end

	-- 根据页面尺寸和起始行号计算截至行号
	set @_end_row = @_start_row + @pageSize - 1

	-- 动态创建查询命令文本
	set @_page_sql = 
		'select ' + 
			'__pv.* ' + 
		'from ' + 
		'(' + 
			'select ' + 
				'v.*, ' + 
				'(row_number() over(order by ' + @orderName + ' ' + @orderDirection + ')) as __row_no ' + 
			'from ' + 
			'"waln_authority"."v_users_user" as v '+ 
			'where ' + 
				'((@name is null) or (v."LoginName" like @name) or (v."Name" like @name) or (v."Mobile" like @name) or (v."Email" like @name)) and ' + 
				'((@locked is null) or (@locked = 0 and v."LockTime" is null) or (@locked = 1 and v."LockTime" is not null)) ' + 
		') as __pv ' + 
		'where ' + 
			'__row_no >= ' + cast(@_start_row as varchar(20)) + ' and __row_no <= ' + cast(@_end_row as varchar(20));

	set @_params_def = 
		'@name nvarchar(50),' + 
		'@locked bit'; 

	exec "dbo"."sp_executesql" @_page_sql, @_params_def, 
		@name, 
		@locked;

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Add - 添加新用户
 * 
 * # 描述:
 *   添加一个新的用户账号。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_add"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_add" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_add"
	@userId uniqueidentifier, -- 用户 Id 
	@loginName nvarchar(50), -- 登录名称 
	@password nchar(30), -- 登录密码明文 
	@no varchar(20), -- 编号 
	@name nvarchar(50), -- 用户名称 
	@mobile varchar(20), -- 手机号码 
	@email varchar(50), -- 电子邮件地址 
	@loginHomeUrl nvarchar(200), -- 登录后首页 
	@loginHomeDescription nvarchar(100), -- 登录后首页说明 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	
	-- 新用户的主键
    declare @pk bigint = null;
	-- 当前日期时间
    declare @now datetime2 = sysdatetime();

    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @loginName (登录名称)
    if @loginName is null or @loginName = '' 
	begin
        set @_message = N'没有提供登录名称。';
        return;
    end

    -- 验证参数 @password (登录密码明文)
    if @password is null or @password = '' or len(@password) < 6 
	begin
        set @_message = N'没有提供有效的登录密码。';
        return;
    end
    
    -- 检查登录名称是否已经被注册
    if exists (select "PK" from "waln_authority"."users" where "LoginName" = @loginName) 
	begin
        set @_message = concat(N'登录名称 ', @loginName, N' 已经被注册。');
        return;
    end

    -- 验证参数 @name (用户名称)
    if @name is null or @name = '' 
	begin
        set @name = @loginName;
    end

    -- 验证参数 @no (编号)
    if @no is not null and @no <> ''
	begin
		-- 检查手机号码是否已经被注册
		if exists (select "PK" from "waln_authority"."users" where "No" = @no) 
		begin
			set @_message = concat(N'用户编号 ', @no, N' 已经被注册。');
			return;
		end
    end
	else
	begin
		set @no = null;
    end

    -- 验证参数 @mobile (手机号码)
    if @mobile is not null and @mobile <> ''
	begin
		-- 检查手机号码是否已经被注册
		if exists (select "PK" from "waln_authority"."users" where "Mobile" = @mobile) 
		begin
			set @_message = concat(N'手机号码 ', @mobile, N' 已经被注册。');
			return;
		end
    end
	else
	begin
		set @mobile = null;
    end

    -- 验证参数 @email (电子邮件地址)
    if @email is not null and @email <> '' 
	begin
		-- 检查手机号码是否已经被注册
		if exists (select "PK" from "waln_authority"."users" where "Email" = @email) 
		begin
			set @_message = concat(N'电子邮件 ', @email, N' 已经被注册。');
			return;
		end
    end
	else
	begin
		set @email = null;
    end

    insert into "waln_authority"."users" 
    (
        "Id", -- Id 
        "LoginName", -- 登录名称 
        "Password", -- 登录密码密文 
        "No", -- 编号 
        "Name", -- 用户名称 
        "FaceUrl", -- 头像路径 
        "Mobile", -- 手机号码 
        "Email", -- 电子邮件地址 
        "CreateTime", -- 创建时间 
        "LockTime", -- 锁定时间 
        "LoginTime", -- 登录时间 
        "Token", -- 用户令牌 
        "TokenExpireTime", -- 令牌有效期, 
        "LoginHomeUrl", -- 登录后首页
        "LoginHomeDescription" -- 登录后首页描述
    )
    values
    (
        newid(), -- Id 
        @loginName, -- 登录名称 
        "waln_authority"."encrypt_password"(@password), -- 登录密码密文 
        @no, -- 编号 
        @name, -- 用户名称 
        null, -- 头像路径 
        @mobile, -- 手机号码 
        @email, -- 电子邮件地址 
        @now, -- 创建时间 
        null, -- 锁定时间 
        null, -- 登录时间 
        null, -- 用户令牌 
        null, -- 令牌有效期 
        @loginHomeUrl, -- 登录后首页
        @loginHomeDescription -- 登录后首页描述
    );
    
    set @pk = scope_identity();

    -- 返回 Add.User
    select 
        v.* 
    from 
        "waln_authority"."v_users_user" as v 
    where 
        v."PK" = @pk; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'添加了一个新的用户： ', @name, N' (', @loginName, ')。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.users', -- 相关数据库表的名称列表
		'waln_authority.p_users_add', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   Edit - 设置用户资料
 * 
 * # 描述:
 *   设置指定用户的用户资料，不包括登录密码。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_edit"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_edit" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_edit"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@loginName nvarchar(50), -- 登录名称 
	@no varchar(20), -- 编号 
	@name nvarchar(50), -- 用户名称 
	@mobile varchar(20), -- 手机号码 
	@email varchar(50), -- 电子邮件地址 
	@loginHomeUrl nvarchar(200), -- 登录后首页 
	@loginHomeDescription nvarchar(100), -- 登录后首页说明 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供用户的 Id。';
        return;
    end

    -- 验证参数 @loginName (登录名称)
    if @loginName is null or @loginName = '' 
	begin
        set @_message = N'没有提供登录名称。';
        return;
    end

    -- 检查登录名称是否已经被注册
    if exists (select "PK" from "waln_authority"."users" where "LoginName" = @loginName and "Id" <> @id) 
	begin
        set @_message = concat(N'登录名称 ', @loginName, N' 已经被注册。');
        return;
    end

    -- 验证参数 @name (用户名称)
    if @name is null or @name = '' 
	begin
        set @name = @loginName;
    end

    -- 验证参数 @no (编号)
    if @no is not null and @no <> ''
	begin
		-- 检查编号是否已经被注册
		if exists (select "PK" from "waln_authority"."users" where "No" = @no and "Id" <> @id) 
		begin
			set @_message = concat(N'用户编号 ', @no, N' 已经被注册。');
			return;
		end
	end
	else
	begin
		set @no = null;
	end

    -- 验证参数 @mobile (手机号码)
    if @mobile is not null and @mobile <> '' 
	begin
		-- 检查手机号码是否已经被注册
		if exists (select "PK" from "waln_authority"."users" where "Mobile" = @mobile and "Id" <> @id) 
		begin
			set @_message = concat(N'手机号码 ', @mobile, N' 已经被注册。');
			return;
		end
	end
	else
	begin
		set @mobile = null;
	end

    -- 验证参数 @email (电子邮件地址)
    if @email is not null and @email <> '' 
	begin
		-- 检查电子邮件地址是否已经被注册
		if exists (select "PK" from "waln_authority"."users" where "Email" = @email and "Id" <> @id) 
		begin
			set @_message = concat(N'电子邮件 ', @email, N' 已经被注册。');
			return;
		end
	end
	else
	begin
		set @email = null;
	end
		
    update 
        "waln_authority"."users" 
    set 
        "LoginName" = @loginName, 
        "No" = @no, 
        "Name" = @name, 
        "Mobile" = @mobile, 
        "Email" = @email, 
        "LoginHomeUrl" = @loginHomeUrl, 
        "LoginHomeDescription" = @loginHomeDescription 
    where 
        "Id" = @id; 

    -- 返回 Edit.User
    select 
        v.* 
    from 
        "waln_authority"."v_users_user" as v 
    where 
        v."Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'修改了用户的信息，新的用户名为： ', @name, N' (', @loginName, ')。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		4, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.users', -- 相关数据库表的名称列表
		'waln_authority.p_users_edit', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   SetPassword - 设置登录密码
 * 
 * # 描述:
 *   设置指定用户的登录密码。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_setpassword"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_setpassword" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_setpassword"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@password nvarchar(30), -- 登录密码明文
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;

	-- 用户名称
	declare @name varchar(50) = null;
	-- 用户登录名称
    declare @loginName varchar(50) = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供目标用户的 Id。';
        return;
    end

    -- 验证参数 @password (登录密码明文)
    if @password is null or @password = '' or len(@password) < 6 
	begin
        set @_message = N'没有提供有效的登录密码。';
        return;
    end
    
	-- 查询用户名称和登录名称
    select @name = "Name", @loginName = "LoginName"
    from "waln_authority"."users"
    where "Id" = @id;
    
    if @loginName is null 
	begin
        set @_message = N'没有找到目标用户。';
        return;
    end

    update 
        "waln_authority"."users" 
    set 
        "Password" = "waln_authority"."encrypt_password"(@password)
    where 
        "Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'设置了用户： ', @name, N' (', @loginName, ') 的登录密码。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		4, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.users', -- 相关数据库表的名称列表
		'waln_authority.p_users_setpassword', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   SetFace - 设置头像
 * 
 * # 描述:
 *   设置指定用户的用户头像。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_setface"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_setface" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_setface"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@faceUrl nvarchar(200), -- 头像路径 
	@oldFaceUrl nvarchar(200) out, -- 原用户头像路径 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;

	-- 用户主键
    declare @pk bigint = null;
	-- 用户名称
	declare @name varchar(50) = null;
	-- 用户登录名称
    declare @loginName varchar(50) = null;

    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 初始化 out 参数
    set @oldFaceUrl = null; -- 原用户头像路径

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供用户的 Id。';
        return;
    end

	-- 查询用户信息
	select @pk = "PK", @name = "Name", @loginName = "LoginName", @oldFaceUrl = "FaceUrl"
    from "waln_authority"."users" where "Id" = @id;
    
    if @pk is null 
	begin
        set @_message = N'没有找到用户。';
        return;
    end

	-- 更新头像路径
    update 
        "waln_authority"."users" 
    set 
        "FaceUrl" = @faceUrl
    where 
        "PK" = @pk; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'设置了用户： ', @name, N' (', @loginName, ') 的头像。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		4, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.users', -- 相关数据库表的名称列表
		'waln_authority.p_users_setface', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   Lock - 锁定用户
 * 
 * # 描述:
 *   锁定或者解锁用户。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_lock"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_lock" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_lock"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@locked bit, -- 已锁定 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;

	-- 用户名称
	declare @name varchar(50) = null;
	-- 用户登录名称
    declare @loginName varchar(50) = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供用户的 Id。';
        return;
    end

    -- 验证参数 @locked (已锁定)
    if @locked is null 
	begin
        set @_message = N'没有提供用户的锁定状态。';
        return;
    end

	-- 查询用户名称和登录名称
	select @name = "Name", @loginName = "LoginName"
    from "waln_authority"."users" where "Id" = @id;
    
    if @loginName is null 
	begin
        set @_message = N'没有找到用户。';
        return;
    end

	-- 更新锁定状态：如果锁定为 null，表示未锁定，否则表示已经锁定
    update 
        "waln_authority"."users" 
    set 
        "LockTime" = (case when @locked = 1 then sysdatetime() else null end) 
    where 
        "Id" = @id; 

    -- 返回 Lock.User
    select 
        v.* 
    from 
        "waln_authority"."v_users_user" as v 
    where 
        v."Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(case when @locked = 1 then N'锁定' else N'解锁' end, N'了用户： ', @name, N' (', @loginName, ')。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		4, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.users', -- 相关数据库表的名称列表
		'waln_authority.p_users_lock', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   Delete - 删除用户账号
 * 
 * # 描述:
 *   删除指定的用户账号。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_delete"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_delete" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_delete"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;

	-- 用户名称
	declare @name varchar(50) = null;
	-- 用户登录名称
    declare @loginName varchar(50) = null;
	-- 用户登录时间
    declare @loginTime datetime2 = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供要删除的用户的 Id。';
        return;
    end
    
	-- 查询用户信息
	select @name = "Name", @loginName = "LoginName", @loginTime = "LoginTime"
    from "waln_authority"."users" where "Id" = @id;
    
    if @loginName is null 
	begin
        set @_message = N'没有找到用户。';
        return;
    end

	-- 已经登录过的用户不能被删除，因为用户可能已经创建过业务数据
    if @loginTime is not null 
	begin
        set @_message = N'已经登录过系统的用户不能被删除。';
        return;
    end

	-- 删除与用户相关的权限
    delete from "waln_authority"."permissions" where "RoleType" = 4 and "RoleId" = @id;
	-- 删除与用户相关的岗位
    delete from "waln_authority"."userposts" where "UserId" = @id;
	-- 删除用户自身
    delete from "waln_authority"."users" where "Id" = @id; 

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'删除了用户： ', @name, N' (', @loginName, ')。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		8, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.users', -- 相关数据库表的名称列表
		'waln_authority.p_users_delete', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   Apps - App 列表
 * 
 * # 描述:
 *   查询已经安装的所有 App 的列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_apps"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_apps" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_apps"
	@userId uniqueidentifier, -- 用户 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 返回 Apps.List
    select 
        v.* 
    from 
        "waln_authority"."v_users_app" as v; 

    -- 指示操作成功
    set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Modules - 模块列表
 * 
 * # 描述:
 *   查询隶属于指定 App 的模块的列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_modules"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_modules" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_modules"
	@userId uniqueidentifier, -- 用户 Id 
	@appId uniqueidentifier, -- 隶属 App 的 Id 
	@roleId uniqueidentifier, -- 权利对象的 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @appId (隶属 App 的 Id)
    if @appId is null 
	begin
        set @_message = N'没有提供隶属应用的 Id。';
        return;
    end

    -- 验证参数 @roleId (权利对象的 Id)，也就是用户、岗位、部门或组织的 Id
    if @roleId is null 
	begin
        set @_message = N'没有提供权利对象的 Id（用户、岗位、部门或组织的 Id）。';
        return;
    end

    -- 返回 Modules.List
    select 
        v.*,
        -- 身份验证和用户资料模块总是授权给所有用户
        (case when v."Id" in ('11E628C9-C5D9-5C9E-8836-A45E60DCC700' /* 身份验证 */, '11E63583-FEC6-0AF0-A936-A45E60DCC700' /* 用户资料 */) then cast('2016-01-01' as datetime) else p."GrantTime" end) as "GrantTime",
        p."ExpireTime"
    from 
        "waln_authority"."v_users_module" as v 
	left join
		"waln_authority"."permissions" as p on p."ModuleId" = v."Id" and p."RoleType" = 4 and p."RoleId" = @roleId
    where 
        v."AppId" = @appId; 

    -- 指示操作成功
    set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Grant - 授权
 * 
 * # 描述:
 *   为指定的部门、岗位或用户添加一个权限。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_grant"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_grant" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_grant"
	@userId uniqueidentifier, -- 用户 Id 
	@moduleId uniqueidentifier, -- 模块的 Id 
	@roleId uniqueidentifier, -- 权利对象的 Id 
	@expireTime datetime2, -- 有效截止时间 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	
	-- 用户名称
	declare @name varchar(50) = null;
    -- 用户登录名称
	declare @loginName varchar(50) = null;
    -- 授权模块隶属的应用的 Id
    declare @appId uniqueidentifier = null;
    -- 授权模块隶属的应用的名称
    declare @appName varchar(50) = null;
    -- 模块的名称
    declare @moduleName varchar(50) = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @moduleId (模块的 Id)
    if @moduleId is null 
	begin
        set @_message = N'没有提供模块的 Id。';
        return;
    end

    -- 验证参数 @roleId (权利对象的 Id)
    if @roleId is null 
	begin
        set @_message = N'没有提供授权用户的 Id。';
        return;
    end
    
	-- 查询用户信息
    select @name = "Name", @loginName = "LoginName"
    from "waln_authority"."users"
    where "Id" = @roleId;
    
    if @loginName is null 
	begin
        set @_message = N'没有找到目标用户。';
        return;
    end
    
    -- 查询模块信息
    select @appId = "AppId", @moduleName = "Name"
    from "waln_authority"."modules" 
    where "Id" = @moduleId;
    
    if @appId is null 
	begin
        set @_message = N'没有找到目标模块。';
        return;
    end
    
    -- 查询应用名称
    select @appName = "Name"
    from "waln_authority"."apps"
    where "Id" = @appId;
	
	-- 身份验证和用户资料总是授权给用户
    if @moduleId not in ('11E628C9-C5D9-5C9E-8836-A45E60DCC700' /* 身份验证 */, '11E63583-FEC6-0AF0-A936-A45E60DCC700' /* 用户资料 */) and
		not exists (select * from "waln_authority"."permissions" where "ModuleId" = @moduleId and "RoleId" = @roleId and "RoleType" = 4) 
	begin
		insert into "waln_authority"."permissions" 
		(
			"ModuleId", -- 模块的 Id 
			"RoleId", -- 权利对象的 Id 
			"RoleType", -- 权利人类型代码 
			"GrantTime", -- 授权时间 
			"ExpireTime"  -- 有效截止时间 
		)
		values
		(
			@moduleId, -- 模块的 Id 
			@roleId, -- 权利对象的 Id 
			4, -- 权利人类型代码 
			sysdatetime(), -- 授权时间 
			null  -- 有效截止时间 
		);
    end
    
    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'为用户： ', @name, N' (', @loginName, ') 分配了新的权限，目标模块为：', @appName, N'.', @moduleName, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.permissions', -- 相关数据库表的名称列表
		'waln_authority.p_users_grant', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   Revoke - 取消授权
 * 
 * # 描述:
 *   删除一个权限。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_users_revoke"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_users_revoke" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_users_revoke"
	@userId uniqueidentifier, -- 用户 Id 
	@moduleId uniqueidentifier, -- 模块的 Id 
	@roleId uniqueidentifier, -- 权利对象的 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @_log_id bigint = null;
	declare @_log_body nvarchar(4000) = null;
	
	-- 用户名称
	declare @name varchar(50) = null;
    -- 用户登录名称
	declare @loginName varchar(50) = null;
    -- 授权模块隶属的应用的 Id
    declare @appId uniqueidentifier = null;
    -- 授权模块隶属的应用的名称
    declare @appName varchar(50) = null;
    -- 模块名称
    declare @moduleName varchar(50) = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @moduleId (模块的 Id)
    if @moduleId is null 
	begin
        set @_message = N'没有提供模块的 Id。';
        return;
    end

    -- 验证参数 @roleId (权利对象的 Id)
    if @roleId is null 
	begin
        set @_message = N'没有提供授权用户的 Id。';
        return;
    end

	-- 查询用户信息
    select @name = "Name", @loginName = "LoginName"
    from "waln_authority"."users"
    where "Id" = @roleId;
    
    if @loginName is null 
	begin
        set @_message = N'没有找到目标授权用户。';
        return;
    end
    
	-- 查询模块信息
    select @appId = "AppId", @moduleName = "Name"
    from "waln_authority"."modules" 
    where "Id" = @moduleId;
    
    if @appId is null 
	begin
        set @_message = N'没有找到目标模块。';
        return;
    end
    
	-- 查询应用名称
    select @appName = "Name"
    from "waln_authority"."apps"
    where "Id" = @appId;

	-- 身份验证和用户资料总是授权给用户
	if @moduleId not in ('11E628C9-C5D9-5C9E-8836-A45E60DCC700' /* 身份验证 */, '11E63583-FEC6-0AF0-A936-A45E60DCC700' /* 用户资料 */) 
	begin
		delete from 
			"waln_authority"."permissions" 
		where 
			"ModuleId" = @moduleId and
			"RoleId" = @roleId and
			"RoleType" = 4; 
	end

    set @_success = 1;
    
	-- 记录操作日志
	set @_log_body = concat(N'为用户： ', @name, N' (', @loginName, ') 撤销了权限，目标模块为：', @appName, N'.', @moduleName, N'。');
	exec "waln_log"."log_op"
		@userId, -- 当前用户的 Id
		2, -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他
		'waln_authority.permissions', -- 相关数据库表的名称列表
		'waln_authority.p_users_revoke', -- 存储过程名称
		@_success, -- 指示操作是否成功
		@_log_body, -- 详细的操作描述
		@_context, -- 当前环境变量参数
		@_log_id out; -- 日志消息的 Id
end
go

/*
 * # 存储过程 - Sql Server:
 *   List - App 列表
 * 
 * # 描述:
 *   查询所有已经安装的 App，以便管理员能够调整 App 在导航列表中的先后顺序。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_apps_list"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_apps_list" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_apps_list"
	@userId uniqueidentifier, -- 用户 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 验证参数 @userId (用户 Id)
	if @userId is null
	begin
		set @_message = N'没有提供有效的 用户 Id。';
		return;
	end

	-- 返回 List.apps
	select
		v.*
	from
		"waln_authority"."v_apps_app" as v;

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   SetPosition - 设置位置
 * 
 * # 描述:
 *   设置指定 App 在导航列表中的先后顺序。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_apps_setposition"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_apps_setposition" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_apps_setposition"
	@userId uniqueidentifier, -- 用户 Id 
	@id uniqueidentifier, -- Id 
	@position int, -- 位置 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 应用的主键
	declare @pk bigint = null;
	-- 原始位置
	declare @oldPosition int = null;
    
    -- 默认指示操作失败
    set @_success = 0;
    set @_message = N'';

    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供有效的管理员的 Id。';
        return;
    end

    -- 验证参数 @id (Id)
    if @id is null 
	begin
        set @_message = N'没有提供 App 的 Id。';
        return;
    end

    -- 验证参数 @position (位置)
    if @position is null 
	begin
        set @_message = N'没有提供有效的位置。';
        return;
    end
    
    -- 查询原始位置
    select @pk = "PK", @oldPosition = "Position" from "waln_authority"."apps" where "Id" = @id;
	if @pk is null 
	begin
        set @_message = N'没有找到 App。';
        return;
    end
    
    if @oldPosition = @position 
	begin
		-- 不需要更改
		set @_success = 1;
        return;
    end
    
    if @oldPosition < @position 
		-- 向后调整位置，将新旧位置之间的 App 向前调整一个位置
		update "waln_authority"."apps" set "Position" = "Position" - 1 where "Position" > @oldPosition and "Position" <= @position;
    else if @oldPosition > @position
		-- 向前调整位置，将新旧位置之间的 App 向后调整一个位置
		update "waln_authority"."apps" set "Position" = "Position" + 1 where "Position" < @oldPosition and "Position" >= @position;

    -- 设置新位置
	update "waln_authority"."apps" set "Position" = @position where "PK" = @pk; 

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Modules - 模块列表
 * 
 * # 描述:
 *   查询和显示隶属于指定 App 中的所有模块的列表。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_apps_modules"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_apps_modules" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_apps_modules"
	@userId uniqueidentifier, -- 用户 Id 
	@appId uniqueidentifier, -- 隶属 App 的 Id 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 验证参数 @userId (用户 Id)
	if @userId is null
	begin
		set @_message = N'没有提供有效的 用户 Id。';
		return;
	end

	-- 验证参数 @appId (隶属 App 的 Id)
	if @appId is null
	begin
		set @_message = N'没有提供 隶属 App 的 Id。';
		return;
	end

	-- 返回 Modules.list
	select
		v.*
	from
		"waln_authority"."v_apps_module" as v
	where
		v."AppId" = @appId;

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   Set - 设置资料
 * 
 * # 描述:
 *   设置当前用户的资料。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_profile_set"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_profile_set" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_profile_set"
	@userId uniqueidentifier, -- 用户 Id 
	@loginName nvarchar(50), -- 登录名称 
	@name nvarchar(50), -- 用户名称 
	@mobile varchar(20), -- 手机号码 
	@email varchar(50), -- 电子邮件地址 
	@loginHomeUrl nvarchar(200), -- 登录后首页 
	@loginHomeDescription nvarchar(100), -- 登录后首页说明 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 主键
	declare @pk bigint = null;

	-- 默认指示操作失败
	set @_success = 0;
	set @_message = N'';

	-- 验证参数 @userId (用户 Id)
	if @userId is null
	begin
		set @_message = N'没有提供有效的 用户 Id。';
		return;
	end

	-- 验证参数 @loginName (登录名称)
	if @loginName is null or @loginName = ''
	begin
		set @_message = N'没有提供 登录名称。';
		return;
	end

	-- 查询用户信息
	select @pk = "PK" 
	from "waln_authority"."users" 
	where "Id" = @userId and 
		"LockTime" is null and -- 未被锁定的
		"Token" is not null and -- 已经登录的
		"TokenExpireTime" >= sysdatetime(); -- 登录令牌未过期的

	if @pk is null
	begin
		set @_message = N'没有找到用户或用户尚未登录。';
		return;
	end
 
    -- 检查登录名称是否已经存在
    if exists (select "PK" from "waln_authority"."users" where "LoginName" = @loginName and "PK" <> @pk) 
	begin
        set @_message = concat(N'登录名称 ', @loginName, N' 已经被注册。');
        return;
    end

    -- 验证参数 $mobile (手机号码)
    if @mobile is not null and @mobile <> '' and exists (select "PK" from "waln_authority"."users" where "Mobile" = @mobile and "PK" <> @pk) 
	begin
        set @_message = concat(N'手机号码 ', @mobile, N' 已经被注册');
        return;
    end

    -- 验证参数 $email (电子邮件地址)
    if @email is not null and @email <> '' and exists (select "PK" from "waln_authority"."users" where "Email" = @email and "PK" <> @pk) 
	begin
        set @_message = concat(N'电子邮件 ', @email, N' 已经被注册');
        return;
    end

	if @name is null or @name = ''
	begin
		set @name = @loginName;
	end

	-- 更新用户资料
	update 
		"waln_authority"."users" 
	set 
		"LoginName" = @loginName, 
		"Name" = @name, 
		"Mobile" = @mobile, 
		"Email" = @email, 
		"LoginHomeUrl" = @loginHomeUrl, 
		"LoginHomeDescription" = @loginHomeDescription 
	where 
		"PK" = @pk; 

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   SetPassword - 设置密码
 * 
 * # 描述:
 *   设置当前用户的登录密码。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_profile_setpassword"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_profile_setpassword" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_profile_setpassword"
	@userId uniqueidentifier, -- 用户 Id 
	@oldPassword nvarchar(30), -- 原始密码 
	@newPassword nvarchar(30), -- 新登录密码 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	-- 用户主键
	declare @pk bigint = null;
    
    -- 密码的密文
    declare @cipher binary(36) = null;
    
    set @_success = 0;
    set @_message = N'';
    
    -- 验证参数 @userId (用户 Id)
    if @userId is null 
	begin
        set @_message = N'没有提供用户的 Id。';
        return;
    end

    if @oldPassword is null or len(@oldPassword) < 6 
	begin
        -- 没有提供有效的参数
		set @_message = N'没有提供有效的原登录密码，最少需要 6 个字符。';
        return;
    end
    
    if @newPassword is null or len(@newPassword) < 6 
	begin
        -- 没有提供有效的参数
		set @_message = N'没有提供有效的新登录密码，最少需要 6 个字符。';
        return;
    end
    
	-- 查询密码密文
    select @pk = "PK", @cipher = "Password"
    from "waln_authority"."users" 
    where "Id" = @userId and 
		"LockTime" is null and -- 未被锁定的
		"Token" is not null and -- 已经登录的
		"TokenExpireTime" >= sysdatetime(); -- 登录令牌尚未过期的
    
    if @pk is null 
	begin
		set @_message = N'没有找到用户或者尚未登录。';
        return;
    end
 
    -- 验证原始密码
    if "waln_authority"."check_password"(@oldPassword, @cipher) = 0
	begin
		set @_message = N'原密码不正确。';
        return;
    end
     
    -- 保存新密码的密文
    update "waln_authority"."users" 
	set "Password" = "waln_authority"."encrypt_password"(@newPassword) 
	where "PK" = @pk;

	-- 指示操作成功
	set @_success = 1;
end
go

/*
 * # 存储过程 - Sql Server:
 *   SetFace - 设置头像
 * 
 * # 描述:
 *   设置当前用户的头像。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_authority"."p_profile_setface"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_authority"."p_profile_setface" as begin set nocount on; end');
go

alter procedure "waln_authority"."p_profile_setface"
	@userId uniqueidentifier, -- 用户 Id 
	@faceUrl nvarchar(200), -- 头像路径 
	@oldFaceUrl nvarchar(200) out, -- 原头像路径 
	@_context nvarchar(4000), -- 当前环境变量参数
	@_success bit out, -- 指示操作是否成功 
	@_message nvarchar(4000) out  -- 操作中产生的消息 
as
begin
	-- 不自动向终端返回受影响行数的计数
	set nocount on;

	declare @pk bigint = null;
    
    set @_success = 0;
    set @_message = N'';
    
    -- 返回原头像文件的路径，以便 web server 能够删除物理文件
    set @oldFaceUrl = null;
 
    -- 验证参数 @userId (用户 Id)
    if @userId is null
	begin
        set @_message = N'没有提供用户 Id。';
        return;
    end

    -- 检查目标 App 的开发者是否传入的 developerId
    select @pk = "PK", @oldFaceUrl = "FaceUrl"
    from "waln_authority"."users" 
    where "Id" = @userId and 
		"LockTime" is null and -- 未被锁定的
		"Token" is not null and -- 已经登录的 
		"TokenExpireTime" >= sysdatetime(); -- 登录令牌尚未过期的
    
    if @pk is null 
	begin
        set @_message = N'没有找到用户或者尚未登录。';
        return;
    end
    
    update 
		"waln_authority"."users" 
    set
		"FaceUrl" = @faceUrl
    where "PK" = @pk;

    set @_success = 1;
end
go
